package registry

import (
	"log"
	"net/http"
	"sort"
	"strings"
	"sync"
	"time"
)

type Registry struct {
	//服务超时的时间--如果注册了超过这个时间还没有与注册中心交互的话，那么服务不可用
	timeout time.Duration
	//保护map
	mu sync.Mutex
	servers map[string]*ServerItem
}

type ServerItem struct {
	Addr string
	start time.Time
}

const (
	defaultPath = "/rpc/register"
	defaultTimeout = time.Minute*5
)

func New(timeout time.Duration) *Registry {
	return &Registry{
		servers: make(map[string]*ServerItem),
		timeout: timeout,
	}
}

var DefaultRegister = New(defaultTimeout)

// putServer 添加服务实例
// 如果不存在实例那么加入---如果存在刷新时间
func (r *Registry) putServer(addr string)  {
	r.mu.Lock()
	defer r.mu.Unlock()
	s := r.servers[addr]
	if s == nil {
		r.servers[addr] = &ServerItem{Addr: addr,start: time.Now()}
	} else {
		s.start = time.Now()
	}
}

func (r *Registry) aliveServers() []string {
	r.mu.Lock()
	defer r.mu.Unlock()
	var alive []string
	for addr, s := range r.servers {
		if r.timeout == 0 || s.start.Add(r.timeout).After(time.Now()) {
			alive = append(alive,addr)
		} else {
			//删除以及超时的服务
			delete(r.servers,addr)
		}
	}
	sort.Strings(alive)
	return alive
}

// ServeHTTP 用HTTP来承载通信
func (r *Registry) ServeHTTP(w http.ResponseWriter,req *http.Request)  {
	switch req.Method {
	case "GET":
	//	用自定义header字段 X-Servers 来保存信息
		w.Header().Set("X-Servers",strings.Join(r.aliveServers(),","))
	case "POST":
		addr := req.Header.Get("X-Server")
		if addr == "" {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		r.putServer(addr)
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func (r *Registry) HandleHTTP(registryPath string)  {
	http.Handle(registryPath,r)
	log.Println("rpc registry path: ",registryPath)
}

func HandleHTTP()  {
	DefaultRegister.HandleHTTP(defaultPath)
}

// Heartbeat 定时发送心跳包确定Server是否还在线
func Heartbeat(registry,addr string,duration time.Duration)  {
	if duration == 0 {
		//每5分钟死掉的话 那么就每四分钟发送一次心跳包--确保存活
		duration = defaultTimeout - time.Duration(1)*time.Minute
	}
	var err error
	err = sendHeartbeat(registry,addr)
	go func() {
		t := time.NewTicker(duration)
		for err == nil {
			<-t.C
			err = sendHeartbeat(registry,addr)
		}
	}()
}

// sendHeartbeat 服务端向注册中心发送心跳包
func sendHeartbeat(registry,addr string) error {
	log.Println(addr,"send heart beat to register",registry)
	httpClient := &http.Client{}
	req, _ := http.NewRequest("POST",registry,nil)
	req.Header.Set("X-Server",addr)
	if _,err := httpClient.Do(req);err != nil {
		log.Println("rpc server: heart beat err:",err)
		return err
	}
	return nil
}

